Tidyverse
tidyverse.org defines Tidyverse as
The tidyverse is an opinionated collection of R packages designed for data science. All packages share an underlying design philosophy, grammar, and data structures.
library(tidyverse)
library(dplyr)
library(scater)
We will use single-cell RNA sequencing on 6826 stem cells from Chronic myelomonocytic leukaemia (CMML) patients and healthy controls using the droplet-based, ultra-high-throughput 10x platform. We found substantial inter and intra-patient heterogeneity, with CMML stem cells displaying distinctive transcriptional programs. Compared with normal controls, CMML stem cells exhibited transcriptomes characterized by increased expression of myeloid-lineage and cell cycle genes, and lower expression of genes selectively expressed by normal haematopoietic stem cells.
sce <- readRDS('sce.rds')
sce
class: SingleCellExperiment
dim: 12695 6826
metadata(0):
assays(3): counts logcounts norm_exprs
rownames(12695): FO538757.2 AP006222.2 ... AC004556.1 AC240274.1
rowData names(12): id symbol ... total_counts log10_total_counts
colnames(6826): AAACCTGCACCGATAT-1 AAACGGGCACGACTCG-1 ... TTTGGTTTCATCTGCC-11 TTTGTCAGTAGGAGTC-11
colData names(59): barcode Sample ... sizeFactor cellType
reducedDimNames(1): tSNE
altExpNames(0):
Tibble
- Tibbles are data-frames
tibble() does much less: it never changes the type of the inputs (e.g. it never converts strings to factors!)
- it never changes the names of variables, and it never creates row names.
- tibble can have column names that are not valid R variable names, aka non-syntactic names.
tb <- tibble(
`:)` = "smile",
` ` = "space",
`2000` = "number"
)
tb
Pipe %>%
Pipe %>% passes the output from one stage to the other.
tbl_df(colData(sce))
names(colData(sce))
[1] "barcode" "Sample"
[3] "total_features" "log10_total_features"
[5] "pct_counts_top_50_features" "pct_counts_top_100_features"
[7] "pct_counts_top_200_features" "pct_counts_top_500_features"
[9] "total_features_endogenous" "log10_total_features_endogenous"
[11] "pct_counts_top_50_features_endogenous" "pct_counts_top_100_features_endogenous"
[13] "pct_counts_top_200_features_endogenous" "pct_counts_top_500_features_endogenous"
[15] "total_features_feature_control" "log10_total_features_feature_control"
[17] "total_features_Mt" "log10_total_features_Mt"
[19] "is_cell_control" "total_features_by_counts"
[21] "log10_total_features_by_counts" "total_counts"
[23] "log10_total_counts" "pct_counts_in_top_50_features"
[25] "pct_counts_in_top_100_features" "pct_counts_in_top_200_features"
[27] "pct_counts_in_top_500_features" "total_features_by_counts_endogenous"
[29] "log10_total_features_by_counts_endogenous" "total_counts_endogenous"
[31] "log10_total_counts_endogenous" "pct_counts_endogenous"
[33] "pct_counts_in_top_50_features_endogenous" "pct_counts_in_top_100_features_endogenous"
[35] "pct_counts_in_top_200_features_endogenous" "pct_counts_in_top_500_features_endogenous"
[37] "total_features_by_counts_feature_control" "log10_total_features_by_counts_feature_control"
[39] "total_counts_feature_control" "log10_total_counts_feature_control"
[41] "pct_counts_feature_control" "pct_counts_in_top_50_features_feature_control"
[43] "pct_counts_in_top_100_features_feature_control" "pct_counts_in_top_200_features_feature_control"
[45] "pct_counts_in_top_500_features_feature_control" "total_features_by_counts_Mt"
[47] "log10_total_features_by_counts_Mt" "total_counts_Mt"
[49] "log10_total_counts_Mt" "pct_counts_Mt"
[51] "pct_counts_in_top_50_features_Mt" "pct_counts_in_top_100_features_Mt"
[53] "pct_counts_in_top_200_features_Mt" "pct_counts_in_top_500_features_Mt"
[55] "CellCycle" "Cluster"
[57] "Phase" "sizeFactor"
[59] "cellType"
tbl_df(colData(sce)) %>%
group_by(Sample) %>%
summarise(
total.features = mean(total_features),
total.counts = mean(total_counts)
)
`summarise()` ungrouping output (override with `.groups` argument)
dplyr - Functions as verbs.
The most useful
select(): select columns
mutate(): create new variables, change existing
filter(): subset your data by some criterion
summarize(): summarize your data in some way
group_by(): group your data by a variable
slice(): grab specific rows
select(): select an observation
Some others
count(): count your data
arrange(): arrange your data by a column or variable
distinct(): gather all distinct values of a variable
n_distinct(): count how many distinct values you have (only works with summarize)
n(): count how many observation you have for a subgroup
sample_n(): Grab an N sample of your data
ungroup(): ungroup grouped data by a variable
top_n(): get the top N number of entries from a data frame
To make it easier we copy the metadata for our SingleCellExperiment object sce to d
d <- tbl_df(colData(sce))
Select : To select collumns
select(d, Sample, Cluster, cellType)
d %>%
select(Sample, Cluster, cellType)
Filter : To select rows
d %>%
filter(cellType == "HSC")
d %>%
select(barcode, Sample, total_features, cellType, Cluster) %>%
filter(Sample == "BC572")
NA
d %>%
filter(cellType == "Erythrocytes", pct_counts_Mt > 1.5) %>%
select(barcode, Sample, pct_counts_Mt, cellType, Cluster)
Mutate:
To create new variables in the data table:
d_exp <- d
d_exp <- cbind(d_exp, t(logcounts(sce)[c('KLF4','RUNX1','EGR1'),]))
d_exp
d_exp %>%
mutate(Klf4Diff = abs(KLF4 - RUNX1)) %>%
select(barcode, Sample, cellType, Klf4Diff)
Arrange:
To order the data by a particular variable:
d_exp %>%
mutate(Klf4Diff = abs(KLF4 - RUNX1)) %>%
arrange(desc(Klf4Diff)) %>%
slice(1)
Slice:
To slice your data by rows:
# The top 5 goleadas?
d_exp %>%
mutate(Klf4Diff = abs(KLF4 - RUNX1)) %>%
arrange(desc(Klf4Diff)) %>%
slice(1:5) # slice_max here would also do the trick
# The top 5 goleadas?
d_exp %>%
mutate(Klf4Diff = (KLF4 - RUNX1)) %>%
arrange(desc(Klf4Diff)) %>%
select(barcode, Sample, cellType, Klf4Diff) %>%
slice_min(Klf4Diff, n = 5)
Group by + sumarize : forget about loops
First: group by a particular variables Second: summarize the data with new statistics. Summarize: Turn many rows into one.
Examples:
- min(x) - minimum value of vector x.
- max(x) - maximum value of vector x.
- mean(x) - mean value of vector x.
- median(x) - median value of vector x.
- quantile(x, p) - pth quantile of vector x.
- sd(x) - standard deviation of vector x.
- var(x) - variance of vector x.
- IQR(x) - Inter Quartile Range (IQR) of vector x.
- diff(range(x)) - total range of vector x.
d %>%
group_by(cellType) %>%
summarise(mean_total_counts = mean(total_counts, na.rm = TRUE), sd_total_counts = sd(total_counts),
mean_pct_Mt_count = mean(pct_counts_Mt), count = n()) %>%
#ungroup() %>%
slice_max(., n=20, order_by = mean_total_counts) # note here, it does
`summarise()` ungrouping output (override with `.groups` argument)
Note: mutate() either changes an existing column or adds a new one. summarise() calculates a single value (per group). As you can see, in the first example, new column is added.
d %>%
count(Sample, cellType)
Plotting in R using ggplot2
GGPlot2 is a powerful and a flexible R package, implemented by Hadley Wickham, for producing elegant graphics piece by piece (Wickham et al. 2017).
The gg in ggplot2 means Grammar of Graphics, a graphic concept which describes plots by using a “grammar”. According to the ggplot2 concept, a plot can be divided into different fundamental parts:
Plot = Data + Aesthetics + Geometry
- Data: a data frame
- Aesthetics: used to indicate the x and y variables. It can be also used to control the color, the size and the shape of points, etc…..
- Geometry: corresponds to the type of graphics (scatter plot, histogram, box plot, line plot, ….)
- additional layers for customization — title, labels, axis, etc.
First plotting
The main function in the ggplot2 package is ggplot(), which can be used to initialize the plotting system with data and x/y variables.
ggplot() creates a coordinate system to which you can add layers to. The first argument of ggplot() is the dataset to use in the graph. So ggplot(data = d_exp) creates an empty graph.
For example, the following R code takes the KLF4 and RUNX1 data set to initialize the ggplot and then a layer (geom_point()) is added onto the ggplot to create a scatter plot of x = KLF4 by y = RUNX1:
- Data=
d_exp
- Aesthetic=: aes(x=KLF4, y=RUNX1)
- Geometry=
geom_point()
ggplot(d_exp)

ggplot(d_exp, aes(x=KLF4, y=RUNX1))

ggplot(d_exp, aes(x=KLF4, y=RUNX1)) + geom_point()

ggplot(d_exp, aes(x=KLF4, y=RUNX1)) + geom_point(size = 1.2, color = "steelblue", shape = 21)

It’s also possible to control points shape and color by a grouping variable (here, Sample). For example, in the code below, we map points color and shape to the datasets grouping variable.
Note that, a ggplot can be holded in a variable, say p, to be printed later
# Control points color by groups
ggplot(d_exp, aes(x=KLF4, y=RUNX1))+
geom_point(aes(color = Sample))

# Change the default color manually.
# Use the scale_color_manual() function
p <- ggplot(d_exp, aes(x=KLF4, y=RUNX1))+
geom_point(aes(color = Sample))+
scale_color_manual(values = c("#00AFBB", "#E7B800", "#FC4E07", "#A2AFBB", "#17B8AB", "#3F4E77", "#FCFA27", "#BFAFFB", "#69B89B", "#7F4E97"))
print(p)

GGPlot theme
Note that, the default theme of ggplots is theme_gray() (or theme_grey()), which is theme with grey background and white grid lines. More themes are available for professional presentations or publications. These include: theme_bw(), theme_classic() and theme_minimal().
To change the theme of a given ggplot (p), use this: p + theme_classic().
p <- ggplot(d_exp, aes(x=KLF4, y=RUNX1))+
geom_point(aes(color = Sample))+
scale_color_manual(values = c("#00AFBB", "#E7B800", "#FC4E07", "#A2AFBB", "#17B8AB", "#3F4E77", "#FCFA27", "#BFAFFB", "#69B89B", "#7F4E97"))
p <- p + theme_classic()
print(p)

df <- reducedDim(sce)
head(df)
[,1] [,2]
AAACCTGCACCGATAT-1 31.75737 41.45222
AAACGGGCACGACTCG-1 39.95130 44.24846
AAAGCAATCCTAAGTG-1 35.30692 40.31110
AAAGTAGGTGATGATA-1 37.10719 43.00212
AAAGTAGTCTCGCTTG-1 40.69935 44.52304
AACACGTGTTGGTAAA-1 38.65847 31.53103
Adding layers to ggplot, Lines (Prediction Line)
A plot constructed with ggplot can have more than one geom. In that case the mappings established in the ggplot() call are plot defaults that can be added to or overridden. Our plot could use a regression line:
d_exp$pred.SC <- predict(lm(RUNX1 ~ KLF4, data = d_exp))
ggplot(d_exp, aes(x = KLF4, y = RUNX1)) +
geom_point(aes(color = Sample)) +
geom_line(aes(y = pred.SC)) +
theme_classic()

Title, xlab & ylab
df <- as.data.frame(reducedDim(sce))
df$Sample <- colData(sce)$Sample
p <- ggplot(df, aes(x=V1, y=V2))+
geom_point(size = 0.4, aes(color = Sample))+
scale_color_manual(values = c("#00AFBB", "#E7B800", "#FC4E07", "#A2AFBB", "#17B8AB", "#3F4E77", "#FCFA27", "#BFAFFB", "#69B89B", "#7F4E97")) +
ggtitle('t-SNE plot for Samples') +
xlab('tSNE-1') +
ylab('tSNE-2') +
theme_classic()
print(p)

df <- as.data.frame(reducedDim(sce))
df$Sample <- colData(sce)$Sample
p <- ggplot(df, aes(x=V1, y=V2))+
geom_point(size = 0.4, aes(color = Sample))+
scale_color_manual(values = c("#00AFBB", "#E7B800", "#FC4E07", "#A2AFBB", "#17B8AB", "#3F4E77", "#FCFA27", "#BFAFFB", "#69B89B", "#7F4E97")) +
ggtitle('t-SNE plot for Samples') +
xlab('tSNE-1') +
ylab('tSNE-2') +
theme_classic() +
guides(colour = guide_legend(override.aes = list(size=4)))
p

Histogram
ggplot(d_exp, aes(x=total_counts)) + geom_histogram() + theme_classic()

Density plot
df <- data.frame(x=log10(sce$total_counts+1), Sample = sce$Sample)
ggplot(df,
aes(x = x, fill = as.factor(Sample))) +
geom_density(alpha = 0.5) +
labs(x = expression('log'[10]*'(Library Size)'), title = "Total reads density", fill = "Sample") +
theme_classic(base_size = 14) + # Setting the base size text for plots
scale_fill_manual(values = c("#00AFBB", "#E7B800", "#FC4E07", "#A2AFBB", "#17B8AB", "#3F4E77", "#FCFA27", "#BFAFFB", "#69B89B", "#7F4E97")) # Need to set fill manual

Facet
df <- data.frame(x=log10(sce$total_counts+1), Sample = sce$Sample)
ggplot(df,
aes(x = x, fill = as.factor(Sample))) +
geom_density(alpha = 0.5) +
labs(x = expression('log'[10]*'(Library Size)'), title = "Total reads density", fill = "Sample") +
theme_classic(base_size = 14) + # Setting the base size text for plots
scale_fill_manual(values = c("#00AFBB", "#E7B800", "#FC4E07", "#A2AFBB", "#17B8AB", "#3F4E77", "#FCFA27", "#BFAFFB", "#69B89B", "#7F4E97")) + # Need to set fill manual
facet_wrap(~Sample)

LS0tCnRpdGxlOiAiVGlkeXZlcnNlICYgZ2dwbG90MiAtIElDRCBCb290Y2FtcCIKYXV0aG9yOiAiU3llZCBNdXJ0dXphIEJha2VyIgpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiwgJVknKWAiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdGhlbWU6IGZsYXRseQogICAgdG9jOiB5ZXMKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IHRydWUKICAgICAgc21vb3RoX3Njcm9sbDogdHJ1ZQplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCgoKIyBUaWR5dmVyc2UKdGlkeXZlcnNlLm9yZyBkZWZpbmVzIFRpZHl2ZXJzZSBhcwoKPiBUaGUgdGlkeXZlcnNlIGlzIGFuIG9waW5pb25hdGVkIGNvbGxlY3Rpb24gb2YgUiBwYWNrYWdlcyBkZXNpZ25lZCBmb3IgZGF0YSBzY2llbmNlLiBBbGwgcGFja2FnZXMgc2hhcmUgYW4gdW5kZXJseWluZyBkZXNpZ24gcGhpbG9zb3BoeSwgZ3JhbW1hciwgYW5kIGRhdGEgc3RydWN0dXJlcy4KCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShkcGx5cikKbGlicmFyeShzY2F0ZXIpCmBgYAoKV2Ugd2lsbCB1c2Ugc2luZ2xlLWNlbGwgUk5BIHNlcXVlbmNpbmcgb24gNjgyNiBzdGVtIGNlbGxzIGZyb20gQ2hyb25pYyBteWVsb21vbm9jeXRpYyBsZXVrYWVtaWEgKENNTUwpIHBhdGllbnRzIGFuZCBoZWFsdGh5IGNvbnRyb2xzIHVzaW5nIHRoZSBkcm9wbGV0LWJhc2VkLCB1bHRyYS1oaWdoLXRocm91Z2hwdXQgMTB4IHBsYXRmb3JtLiBXZSBmb3VuZCBzdWJzdGFudGlhbCBpbnRlciBhbmQgaW50cmEtcGF0aWVudCBoZXRlcm9nZW5laXR5LCB3aXRoIENNTUwgc3RlbSBjZWxscyBkaXNwbGF5aW5nIGRpc3RpbmN0aXZlIHRyYW5zY3JpcHRpb25hbCBwcm9ncmFtcy4gQ29tcGFyZWQgd2l0aCBub3JtYWwgY29udHJvbHMsIENNTUwgc3RlbSBjZWxscyBleGhpYml0ZWQgdHJhbnNjcmlwdG9tZXMgY2hhcmFjdGVyaXplZCBieSBpbmNyZWFzZWQgZXhwcmVzc2lvbiBvZiBteWVsb2lkLWxpbmVhZ2UgYW5kIGNlbGwgY3ljbGUgZ2VuZXMsIGFuZCBsb3dlciBleHByZXNzaW9uIG9mIGdlbmVzIHNlbGVjdGl2ZWx5IGV4cHJlc3NlZCBieSBub3JtYWwgaGFlbWF0b3BvaWV0aWMgc3RlbSBjZWxscy4gCgpgYGB7cn0Kc2NlIDwtIHJlYWRSRFMoJ3NjZS5yZHMnKQpzY2UKYGBgCgojIyBUaWJibGUKLSBUaWJibGVzIGFyZSBkYXRhLWZyYW1lcwotIGB0aWJibGUoKWAgZG9lcyBtdWNoIGxlc3M6IGl0IG5ldmVyIGNoYW5nZXMgdGhlIHR5cGUgb2YgdGhlIGlucHV0cyAoZS5nLiBpdCBuZXZlciBjb252ZXJ0cyBzdHJpbmdzIHRvIGZhY3RvcnMhKQotIGl0IG5ldmVyIGNoYW5nZXMgdGhlIG5hbWVzIG9mIHZhcmlhYmxlcywgYW5kIGl0IG5ldmVyIGNyZWF0ZXMgcm93IG5hbWVzLgotIHRpYmJsZSBjYW4gaGF2ZSBjb2x1bW4gbmFtZXMgdGhhdCBhcmUgbm90IHZhbGlkIFIgdmFyaWFibGUgbmFtZXMsIGFrYSAqbm9uLXN5bnRhY3RpYyogbmFtZXMuCmBgYHtyfQp0YiA8LSB0aWJibGUoCiAgYDopYCA9ICJzbWlsZSIsIAogIGAgYCA9ICJzcGFjZSIsCiAgYDIwMDBgID0gIm51bWJlciIKKQp0YgpgYGAKCiMjIFBpcGUgYCU+JWAKUGlwZSBgJT4lYCBwYXNzZXMgdGhlIG91dHB1dCBmcm9tIG9uZSBzdGFnZSB0byB0aGUgb3RoZXIuCmBgYHtyfQp0YmxfZGYoY29sRGF0YShzY2UpKQpgYGAKYGBge3J9Cm5hbWVzKGNvbERhdGEoc2NlKSkKYGBgCgoKYGBge3J9CnRibF9kZihjb2xEYXRhKHNjZSkpICU+JQogIGdyb3VwX2J5KFNhbXBsZSkgJT4lCiAgc3VtbWFyaXNlKAogICAgdG90YWwuZmVhdHVyZXMgPSBtZWFuKHRvdGFsX2ZlYXR1cmVzKSwKICAgIHRvdGFsLmNvdW50cyA9IG1lYW4odG90YWxfY291bnRzKQogICkKYGBgCgojIyBgZHBseXJgIC0gRnVuY3Rpb25zIGFzIHZlcmJzLgoKX19UaGUgbW9zdCB1c2VmdWxfXwoKLSBgc2VsZWN0KClgOiBzZWxlY3QgY29sdW1ucwotIGBtdXRhdGUoKWA6IGNyZWF0ZSBuZXcgdmFyaWFibGVzLCBjaGFuZ2UgZXhpc3RpbmcKLSBgZmlsdGVyKClgOiBzdWJzZXQgeW91ciBkYXRhIGJ5IHNvbWUgY3JpdGVyaW9uCi0gYHN1bW1hcml6ZSgpYDogc3VtbWFyaXplIHlvdXIgZGF0YSBpbiBzb21lIHdheQotIGBncm91cF9ieSgpYDogZ3JvdXAgeW91ciBkYXRhIGJ5IGEgdmFyaWFibGUKLSBgc2xpY2UoKWA6IGdyYWIgc3BlY2lmaWMgcm93cwotIGBzZWxlY3QoKWA6IHNlbGVjdCBhbiBvYnNlcnZhdGlvbgoKX19Tb21lIG90aGVyc19fCgotIGBjb3VudCgpYDogY291bnQgeW91ciBkYXRhCi0gYGFycmFuZ2UoKWA6IGFycmFuZ2UgeW91ciBkYXRhIGJ5IGEgY29sdW1uIG9yIHZhcmlhYmxlCi0gYGRpc3RpbmN0KClgOiBnYXRoZXIgYWxsIGRpc3RpbmN0IHZhbHVlcyBvZiBhIHZhcmlhYmxlCi0gYG5fZGlzdGluY3QoKWA6IGNvdW50IGhvdyBtYW55IGRpc3RpbmN0IHZhbHVlcyB5b3UgaGF2ZSAob25seSB3b3JrcyB3aXRoIHN1bW1hcml6ZSkKLSBgbigpYDogY291bnQgaG93IG1hbnkgb2JzZXJ2YXRpb24geW91IGhhdmUgZm9yIGEgc3ViZ3JvdXAKLSBgc2FtcGxlX24oKWA6IEdyYWIgYW4gTiBzYW1wbGUgb2YgeW91ciBkYXRhCi0gYHVuZ3JvdXAoKWA6IHVuZ3JvdXAgZ3JvdXBlZCBkYXRhIGJ5IGEgdmFyaWFibGUKLSBgdG9wX24oYCk6IGdldCB0aGUgdG9wIE4gbnVtYmVyIG9mIGVudHJpZXMgZnJvbSBhIGRhdGEgZnJhbWUKCl9fVG8gbWFrZSBpdCBlYXNpZXIgd2UgY29weSB0aGUgbWV0YWRhdGEgZm9yIG91ciBgU2luZ2xlQ2VsbEV4cGVyaW1lbnRgIG9iamVjdCBgc2NlYCB0byBgZGAgX18KCmBgYHtyfQpkIDwtIHRibF9kZihjb2xEYXRhKHNjZSkpCmBgYAoKIyMjIGBTZWxlY3RgIDogVG8gc2VsZWN0IGNvbGx1bW5zCmBgYHtyfQpzZWxlY3QoZCwgU2FtcGxlLCBDbHVzdGVyLCBjZWxsVHlwZSkKYGBgCgpgYGB7cn0KZCAlPiUgCiAgc2VsZWN0KFNhbXBsZSwgQ2x1c3RlciwgY2VsbFR5cGUpCmBgYAoKIyMjIGBGaWx0ZXJgIDogVG8gc2VsZWN0IHJvd3MKYGBge3J9CmQgJT4lIAogIGZpbHRlcihjZWxsVHlwZSA9PSAiSFNDIikKYGBgCgpgYGB7cn0KZCAlPiUgCiAgc2VsZWN0KGJhcmNvZGUsIFNhbXBsZSwgdG90YWxfZmVhdHVyZXMsIGNlbGxUeXBlLCBDbHVzdGVyKSAlPiUKICBmaWx0ZXIoU2FtcGxlID09ICJCQzU3MiIpCgpgYGAKCmBgYHtyfQpkICU+JSAKICBmaWx0ZXIoY2VsbFR5cGUgPT0gIkVyeXRocm9jeXRlcyIsIHBjdF9jb3VudHNfTXQgPiAxLjUpICU+JSAKICBzZWxlY3QoYmFyY29kZSwgU2FtcGxlLCBwY3RfY291bnRzX010LCBjZWxsVHlwZSwgQ2x1c3RlcikKYGBgCgojIyMgYE11dGF0ZWA6IApUbyBjcmVhdGUgbmV3IHZhcmlhYmxlcyBpbiB0aGUgZGF0YSB0YWJsZToKYGBge3J9CmRfZXhwIDwtIGQKZF9leHAgPC0gY2JpbmQoZF9leHAsIHQobG9nY291bnRzKHNjZSlbYygnS0xGNCcsJ1JVTlgxJywnRUdSMScpLF0pKQpgYGAKCmBgYHtyfQpkX2V4cApgYGAKCmBgYHtyfQpkX2V4cCAlPiUgCiAgbXV0YXRlKEtsZjREaWZmID0gYWJzKEtMRjQgLSBSVU5YMSkpICU+JQogIHNlbGVjdChiYXJjb2RlLCBTYW1wbGUsIGNlbGxUeXBlLCBLbGY0RGlmZikKYGBgCgoKCiMjIyBgQXJyYW5nZWA6IApUbyBvcmRlciB0aGUgZGF0YSBieSBhIHBhcnRpY3VsYXIgdmFyaWFibGU6CgpgYGB7cn0KZF9leHAgJT4lIAogIG11dGF0ZShLbGY0RGlmZiA9IGFicyhLTEY0IC0gUlVOWDEpKSAlPiUgCiAgYXJyYW5nZShkZXNjKEtsZjREaWZmKSkgJT4lIAogIHNsaWNlKDEpCmBgYAojIyMgYFNsaWNlYDogClRvIHNsaWNlIHlvdXIgZGF0YSBieSByb3dzOgoKYGBge3J9CiMgVGhlIHRvcCA1IGdvbGVhZGFzPwpkX2V4cCAlPiUgCiAgbXV0YXRlKEtsZjREaWZmID0gYWJzKEtMRjQgLSBSVU5YMSkpICU+JSAKICBhcnJhbmdlKGRlc2MoS2xmNERpZmYpKSAlPiUgCiAgc2xpY2UoMTo1KSAgIyBzbGljZV9tYXggaGVyZSB3b3VsZCBhbHNvIGRvIHRoZSB0cmljawpgYGAKCmBgYHtyfQojIFRoZSB0b3AgNSBnb2xlYWRhcz8KZF9leHAgJT4lIAogIG11dGF0ZShLbGY0RGlmZiA9IChLTEY0IC0gUlVOWDEpKSAlPiUgCiAgYXJyYW5nZShkZXNjKEtsZjREaWZmKSkgJT4lIAogIHNlbGVjdChiYXJjb2RlLCBTYW1wbGUsIGNlbGxUeXBlLCBLbGY0RGlmZikgJT4lCiAgc2xpY2VfbWluKEtsZjREaWZmLCBuID0gNSkKYGBgCgoKIyMjIEdyb3VwIGJ5ICsgc3VtYXJpemUgOiBmb3JnZXQgYWJvdXQgbG9vcHMKCl9fRmlyc3RfXzogZ3JvdXAgYnkgYSBwYXJ0aWN1bGFyIHZhcmlhYmxlcwpfX1NlY29uZF9fOiBzdW1tYXJpemUgdGhlIGRhdGEgd2l0aCBuZXcgc3RhdGlzdGljcy4KX19TdW1tYXJpemVfXzogVHVybiBtYW55IHJvd3MgaW50byBvbmUuCgpFeGFtcGxlczoKCi0gbWluKHgpIC0gbWluaW11bSB2YWx1ZSBvZiB2ZWN0b3IgeC4KLSBtYXgoeCkgLSBtYXhpbXVtIHZhbHVlIG9mIHZlY3RvciB4LgotIG1lYW4oeCkgLSBtZWFuIHZhbHVlIG9mIHZlY3RvciB4LgotIG1lZGlhbih4KSAtIG1lZGlhbiB2YWx1ZSBvZiB2ZWN0b3IgeC4KLSBxdWFudGlsZSh4LCBwKSAtIHB0aCBxdWFudGlsZSBvZiB2ZWN0b3IgeC4KLSBzZCh4KSAtIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB2ZWN0b3IgeC4KLSB2YXIoeCkgLSB2YXJpYW5jZSBvZiB2ZWN0b3IgeC4KLSBJUVIoeCkgLSBJbnRlciBRdWFydGlsZSBSYW5nZSAoSVFSKSBvZiB2ZWN0b3IgeC4KLSBkaWZmKHJhbmdlKHgpKSAtIHRvdGFsIHJhbmdlIG9mIHZlY3RvciB4LgoKYGBge3J9CmQgJT4lIAogIGdyb3VwX2J5KGNlbGxUeXBlKSAlPiUgCiAgc3VtbWFyaXNlKG1lYW5fdG90YWxfY291bnRzID0gbWVhbih0b3RhbF9jb3VudHMsIG5hLnJtID0gVFJVRSksIHNkX3RvdGFsX2NvdW50cyA9IHNkKHRvdGFsX2NvdW50cyksIAogICAgIG1lYW5fcGN0X010X2NvdW50ID0gbWVhbihwY3RfY291bnRzX010KSwgY291bnQgPSBuKCkpICU+JSAKICAjdW5ncm91cCgpICU+JSAKICBzbGljZV9tYXgoLiwgbj0yMCwgb3JkZXJfYnkgPSBtZWFuX3RvdGFsX2NvdW50cykgICMgbm90ZSBoZXJlLCBpdCBkb2VzIApgYGAKCl9fTm90ZTogYG11dGF0ZSgpYCBlaXRoZXIgY2hhbmdlcyBhbiBleGlzdGluZyBjb2x1bW4gb3IgYWRkcyBhIG5ldyBvbmUuIGBzdW1tYXJpc2UoKWAgY2FsY3VsYXRlcyBhIHNpbmdsZSB2YWx1ZSAocGVyIGdyb3VwKS4gQXMgeW91IGNhbiBzZWUsIGluIHRoZSBmaXJzdCBleGFtcGxlLCBuZXcgY29sdW1uIGlzIGFkZGVkLl9fCgpgYGB7cn0KZCAlPiUgCmNvdW50KFNhbXBsZSwgY2VsbFR5cGUpCmBgYAoKIyBQbG90dGluZyBpbiBSIHVzaW5nIGBnZ3Bsb3QyYAoKYEdHUGxvdDJgIGlzIGEgcG93ZXJmdWwgYW5kIGEgZmxleGlibGUgUiBwYWNrYWdlLCBpbXBsZW1lbnRlZCBieSBIYWRsZXkgV2lja2hhbSwgZm9yIHByb2R1Y2luZyBlbGVnYW50IGdyYXBoaWNzIHBpZWNlIGJ5IHBpZWNlIChXaWNraGFtIGV0IGFsLiAyMDE3KS4KClRoZSBgZ2dgIGluIGBnZ3Bsb3QyYCBtZWFucyBHcmFtbWFyIG9mIEdyYXBoaWNzLCBhIGdyYXBoaWMgY29uY2VwdCB3aGljaCBkZXNjcmliZXMgcGxvdHMgYnkgdXNpbmcgYSDigJxncmFtbWFy4oCdLiBBY2NvcmRpbmcgdG8gdGhlIGdncGxvdDIgY29uY2VwdCwgYSBwbG90IGNhbiBiZSBkaXZpZGVkIGludG8gZGlmZmVyZW50IGZ1bmRhbWVudGFsIHBhcnRzOiAKCj4gUGxvdCA9IERhdGEgKyBBZXN0aGV0aWNzICsgR2VvbWV0cnkKCgoxLiBfX0RhdGE6X18gYSBkYXRhIGZyYW1lCjIuIF9fQWVzdGhldGljczpfXyB1c2VkIHRvIGluZGljYXRlIHRoZSB4IGFuZCB5IHZhcmlhYmxlcy4gSXQgY2FuIGJlIGFsc28gdXNlZCB0byBjb250cm9sIHRoZSBjb2xvciwgdGhlIHNpemUgYW5kIHRoZSBzaGFwZSBvZiBwb2ludHMsIGV0Y+KApi4uCjMuIF9fR2VvbWV0cnk6X18gY29ycmVzcG9uZHMgdG8gdGhlIHR5cGUgb2YgZ3JhcGhpY3MgKHNjYXR0ZXIgcGxvdCwgaGlzdG9ncmFtLCBib3ggcGxvdCwgbGluZSBwbG90LCDigKYuKQo0LiBhZGRpdGlvbmFsIGxheWVycyBmb3IgY3VzdG9taXphdGlvbiDigJQgdGl0bGUsIGxhYmVscywgYXhpcywgZXRjLgoKCiMjIEZpcnN0IHBsb3R0aW5nClRoZSBtYWluIGZ1bmN0aW9uIGluIHRoZSBgZ2dwbG90MmAgcGFja2FnZSBpcyBgZ2dwbG90KClgLCB3aGljaCBjYW4gYmUgdXNlZCB0byBpbml0aWFsaXplIHRoZSBwbG90dGluZyBzeXN0ZW0gd2l0aCBfX2RhdGFfXyBhbmQgX194L3lfXyB2YXJpYWJsZXMuCgpgZ2dwbG90KClgIGNyZWF0ZXMgYSBjb29yZGluYXRlIHN5c3RlbSB0byB3aGljaCB5b3UgY2FuIGFkZCBsYXllcnMgdG8uIFRoZSBmaXJzdCBhcmd1bWVudCBvZiBgZ2dwbG90KClgIGlzIHRoZSBkYXRhc2V0IHRvIHVzZSBpbiB0aGUgZ3JhcGguIFNvIGBnZ3Bsb3QoZGF0YSA9IGRfZXhwKWAgY3JlYXRlcyBhbiBlbXB0eSBncmFwaC4KCkZvciBleGFtcGxlLCB0aGUgZm9sbG93aW5nIFIgY29kZSB0YWtlcyB0aGUgYEtMRjRgIGFuZCBgUlVOWDFgIGRhdGEgc2V0IHRvIGluaXRpYWxpemUgdGhlIGBnZ3Bsb3RgIGFuZCB0aGVuIGEgbGF5ZXIgKGdlb21fcG9pbnQoKSkgaXMgYWRkZWQgb250byB0aGUgZ2dwbG90IHRvIGNyZWF0ZSBhIHNjYXR0ZXIgcGxvdCBvZiB4ID0gS0xGNCBieSB5ID0gUlVOWDE6CgoxLiBfX0RhdGE9X18gYGRfZXhwYAoyLiBfX0Flc3RoZXRpYz06X18gYWVzKHg9S0xGNCwgeT1SVU5YMSkKMy4gX19HZW9tZXRyeT1fXyBgZ2VvbV9wb2ludCgpYAoKYGBge3J9CmdncGxvdChkX2V4cCkKYGBgCgoKYGBge3J9CmdncGxvdChkX2V4cCwgYWVzKHg9S0xGNCwgeT1SVU5YMSkpCmBgYAoKYGBge3J9CmdncGxvdChkX2V4cCwgYWVzKHg9S0xGNCwgeT1SVU5YMSkpICsgZ2VvbV9wb2ludCgpCmBgYAoKYGBge3J9CmdncGxvdChkX2V4cCwgYWVzKHg9S0xGNCwgeT1SVU5YMSkpICsgZ2VvbV9wb2ludChzaXplID0gMS4yLCBjb2xvciA9ICJzdGVlbGJsdWUiLCBzaGFwZSA9IDIxKQpgYGAKCkl04oCZcyBhbHNvIHBvc3NpYmxlIHRvIGNvbnRyb2wgcG9pbnRzIHNoYXBlIGFuZCBjb2xvciBieSBhIGdyb3VwaW5nIHZhcmlhYmxlIChoZXJlLCBgU2FtcGxlYCkuIEZvciBleGFtcGxlLCBpbiB0aGUgY29kZSBiZWxvdywgd2UgbWFwIHBvaW50cyBgY29sb3JgIGFuZCBgc2hhcGVgIHRvIHRoZSBkYXRhc2V0cyBncm91cGluZyB2YXJpYWJsZS4KCk5vdGUgdGhhdCwgYSBgZ2dwbG90YCBjYW4gYmUgaG9sZGVkIGluIGEgdmFyaWFibGUsIHNheSBgcGAsIHRvIGJlIHByaW50ZWQgbGF0ZXIKCmBgYHtyfQojIENvbnRyb2wgcG9pbnRzIGNvbG9yIGJ5IGdyb3VwcwpnZ3Bsb3QoZF9leHAsIGFlcyh4PUtMRjQsIHk9UlVOWDEpKSsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IFNhbXBsZSkpCgojIENoYW5nZSB0aGUgZGVmYXVsdCBjb2xvciBtYW51YWxseS4KIyBVc2UgdGhlIHNjYWxlX2NvbG9yX21hbnVhbCgpIGZ1bmN0aW9uCnAgPC0gZ2dwbG90KGRfZXhwLCBhZXMoeD1LTEY0LCB5PVJVTlgxKSkrCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBTYW1wbGUpKSsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiIzAwQUZCQiIsICIjRTdCODAwIiwgIiNGQzRFMDciLCAiI0EyQUZCQiIsICIjMTdCOEFCIiwgIiMzRjRFNzciLCAiI0ZDRkEyNyIsICIjQkZBRkZCIiwgIiM2OUI4OUIiLCAiIzdGNEU5NyIpKQpwcmludChwKQpgYGAKCiMjIEdHUGxvdCB0aGVtZQoKTm90ZSB0aGF0LCB0aGUgZGVmYXVsdCB0aGVtZSBvZiBnZ3Bsb3RzIGlzIGB0aGVtZV9ncmF5KClgIChvciBgdGhlbWVfZ3JleSgpYCksIHdoaWNoIGlzIHRoZW1lIHdpdGggZ3JleSBiYWNrZ3JvdW5kIGFuZCB3aGl0ZSBncmlkIGxpbmVzLiBNb3JlIHRoZW1lcyBhcmUgYXZhaWxhYmxlIGZvciBwcm9mZXNzaW9uYWwgcHJlc2VudGF0aW9ucyBvciBwdWJsaWNhdGlvbnMuIFRoZXNlIGluY2x1ZGU6IGB0aGVtZV9idygpYCwgYHRoZW1lX2NsYXNzaWMoKWAgYW5kIGB0aGVtZV9taW5pbWFsKClgLgoKVG8gY2hhbmdlIHRoZSB0aGVtZSBvZiBhIGdpdmVuIGdncGxvdCAocCksIHVzZSB0aGlzOiBgcCArIHRoZW1lX2NsYXNzaWMoKWAuIAoKYGBge3J9CnAgPC0gZ2dwbG90KGRfZXhwLCBhZXMoeD1LTEY0LCB5PVJVTlgxKSkrCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBTYW1wbGUpKSsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiIzAwQUZCQiIsICIjRTdCODAwIiwgIiNGQzRFMDciLCAiI0EyQUZCQiIsICIjMTdCOEFCIiwgIiMzRjRFNzciLCAiI0ZDRkEyNyIsICIjQkZBRkZCIiwgIiM2OUI4OUIiLCAiIzdGNEU5NyIpKQpwIDwtIHAgKyB0aGVtZV9jbGFzc2ljKCkKcHJpbnQocCkKCmBgYApgYGB7cn0KZGYgPC0gcmVkdWNlZERpbShzY2UpCmhlYWQoZGYpCmBgYAoKIyMgQWRkaW5nIGxheWVycyB0byBnZ3Bsb3QsIExpbmVzIChQcmVkaWN0aW9uIExpbmUpCkEgcGxvdCBjb25zdHJ1Y3RlZCB3aXRoIGdncGxvdCBjYW4gaGF2ZSBtb3JlIHRoYW4gb25lIGdlb20uIEluIHRoYXQgY2FzZSB0aGUgbWFwcGluZ3MgZXN0YWJsaXNoZWQgaW4gdGhlIGBnZ3Bsb3QoKWAgY2FsbCBhcmUgcGxvdCBkZWZhdWx0cyB0aGF0IGNhbiBiZSBhZGRlZCB0byBvciBvdmVycmlkZGVuLiBPdXIgcGxvdCBjb3VsZCB1c2UgYSByZWdyZXNzaW9uIGxpbmU6CmBgYHtyfQpkX2V4cCRwcmVkLlNDIDwtIHByZWRpY3QobG0oUlVOWDEgfiBLTEY0LCBkYXRhID0gZF9leHApKQoKZ2dwbG90KGRfZXhwLCBhZXMoeCA9IEtMRjQsIHkgPSBSVU5YMSkpICsgCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBTYW1wbGUpKSArCiAgZ2VvbV9saW5lKGFlcyh5ID0gcHJlZC5TQykpICsKICB0aGVtZV9jbGFzc2ljKCkKYGBgCgoKIyMgYFRpdGxlYCwgYHhsYWJgICYgYHlsYWJgCmBgYHtyfQpkZiA8LSBhcy5kYXRhLmZyYW1lKHJlZHVjZWREaW0oc2NlKSkKZGYkU2FtcGxlIDwtIGNvbERhdGEoc2NlKSRTYW1wbGUKcCA8LSBnZ3Bsb3QoZGYsIGFlcyh4PVYxLCB5PVYyKSkrCiAgZ2VvbV9wb2ludChzaXplID0gMC40LCBhZXMoY29sb3IgPSBTYW1wbGUpKSsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiIzAwQUZCQiIsICIjRTdCODAwIiwgIiNGQzRFMDciLCAiI0EyQUZCQiIsICIjMTdCOEFCIiwgIiMzRjRFNzciLCAiI0ZDRkEyNyIsICIjQkZBRkZCIiwgIiM2OUI4OUIiLCAiIzdGNEU5NyIpKSArIAogIGdndGl0bGUoJ3QtU05FIHBsb3QgZm9yIFNhbXBsZXMnKSArIAogIHhsYWIoJ3RTTkUtMScpICsgCiAgeWxhYigndFNORS0yJykgKyAKICB0aGVtZV9jbGFzc2ljKCkKCnAKYGBgCgpgYGB7cn0KZGYgPC0gYXMuZGF0YS5mcmFtZShyZWR1Y2VkRGltKHNjZSkpCmRmJFNhbXBsZSA8LSBjb2xEYXRhKHNjZSkkU2FtcGxlCnAgPC0gZ2dwbG90KGRmLCBhZXMoeD1WMSwgeT1WMikpKwogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNCwgYWVzKGNvbG9yID0gU2FtcGxlKSkrCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIiMwMEFGQkIiLCAiI0U3QjgwMCIsICIjRkM0RTA3IiwgIiNBMkFGQkIiLCAiIzE3QjhBQiIsICIjM0Y0RTc3IiwgIiNGQ0ZBMjciLCAiI0JGQUZGQiIsICIjNjlCODlCIiwgIiM3RjRFOTciKSkgKyAKICBnZ3RpdGxlKCd0LVNORSBwbG90IGZvciBTYW1wbGVzJykgKyAKICB4bGFiKCd0U05FLTEnKSArIAogIHlsYWIoJ3RTTkUtMicpICsgCiAgdGhlbWVfY2xhc3NpYygpICsgCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemU9NCkpKQoKcApgYGAKIyMgSGlzdG9ncmFtCgpgYGB7cn0KZ2dwbG90KGRfZXhwLCBhZXMoeD10b3RhbF9jb3VudHMpKSArIGdlb21faGlzdG9ncmFtKCkgKyB0aGVtZV9jbGFzc2ljKCkgCmBgYAoKCiMjIERlbnNpdHkgcGxvdApgYGB7cn0KZGYgPC0gZGF0YS5mcmFtZSh4PWxvZzEwKHNjZSR0b3RhbF9jb3VudHMrMSksIFNhbXBsZSA9IHNjZSRTYW1wbGUpCmdncGxvdChkZiwKICAgICAgIGFlcyh4ID0geCwgZmlsbCA9IGFzLmZhY3RvcihTYW1wbGUpKSkgKyAKICAgICAgIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuNSkgKwogICAgICAgbGFicyh4ID0gZXhwcmVzc2lvbignbG9nJ1sxMF0qJyhMaWJyYXJ5IFNpemUpJyksIHRpdGxlID0gIlRvdGFsIHJlYWRzIGRlbnNpdHkiLCBmaWxsID0gIlNhbXBsZSIpICsgCiAgICAgICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDE0KSArICMgU2V0dGluZyB0aGUgYmFzZSBzaXplIHRleHQgZm9yIHBsb3RzCiAgICAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjMDBBRkJCIiwgIiNFN0I4MDAiLCAiI0ZDNEUwNyIsICIjQTJBRkJCIiwgIiMxN0I4QUIiLCAiIzNGNEU3NyIsICIjRkNGQTI3IiwgIiNCRkFGRkIiLCAiIzY5Qjg5QiIsICIjN0Y0RTk3IikpICMgTmVlZCB0byBzZXQgZmlsbCBtYW51YWwKYGBgCgojIyBGYWNldAoKYGBge3J9CmRmIDwtIGRhdGEuZnJhbWUoeD1sb2cxMChzY2UkdG90YWxfY291bnRzKzEpLCBTYW1wbGUgPSBzY2UkU2FtcGxlKQpnZ3Bsb3QoZGYsCiAgICAgICBhZXMoeCA9IHgsIGZpbGwgPSBhcy5mYWN0b3IoU2FtcGxlKSkpICsgCiAgICAgICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjUpICsKICAgICAgIGxhYnMoeCA9IGV4cHJlc3Npb24oJ2xvZydbMTBdKicoTGlicmFyeSBTaXplKScpLCB0aXRsZSA9ICJUb3RhbCByZWFkcyBkZW5zaXR5IiwgZmlsbCA9ICJTYW1wbGUiKSArIAogICAgICAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxNCkgKyAjIFNldHRpbmcgdGhlIGJhc2Ugc2l6ZSB0ZXh0IGZvciBwbG90cwogICAgICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiIzAwQUZCQiIsICIjRTdCODAwIiwgIiNGQzRFMDciLCAiI0EyQUZCQiIsICIjMTdCOEFCIiwgIiMzRjRFNzciLCAiI0ZDRkEyNyIsICIjQkZBRkZCIiwgIiM2OUI4OUIiLCAiIzdGNEU5NyIpKSArICAjIE5lZWQgdG8gc2V0IGZpbGwgbWFudWFsIAogIGZhY2V0X3dyYXAoflNhbXBsZSkKYGBgCgojIFN0YXRpc3RpY2FsIFRyYW5zZm9ybWF0aW9ucwojIyBTdGF0aXN0aWNhbCBUcmFuc2Zvcm1hdGlvbnMKU29tZSBwbG90IHR5cGVzIChzdWNoIGFzIHNjYXR0ZXJwbG90cykgZG8gbm90IHJlcXVpcmUgdHJhbnNmb3JtYXRpb25z4oCTZWFjaCBwb2ludCBpcyBwbG90dGVkIGF0IHggYW5kIHkgY29vcmRpbmF0ZXMgZXF1YWwgdG8gdGhlIG9yaWdpbmFsIHZhbHVlLiBPdGhlciBwbG90cywgc3VjaCBhcyBib3hwbG90cywgaGlzdG9ncmFtcywgcHJlZGljdGlvbiBsaW5lcyBldGMuIHJlcXVpcmUgc3RhdGlzdGljYWwgdHJhbnNmb3JtYXRpb25zOgoKLSBmb3IgYSBzbW9vdGhlciB0aGUgeSB2YWx1ZXMgbXVzdCBiZSB0cmFuc2Zvcm1lZCBpbnRvIHByZWRpY3RlZCB2YWx1ZXMKCmBgYHtyfQpnZ3Bsb3QoZF9leHAsIGFlcyh4ID0gS0xGNCwgeSA9IFJVTlgxKSkgKyAKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IFNhbXBsZSkpICsKICBnZW9tX3Ntb290aCgpICsKICB0aGVtZV9jbGFzc2ljKCkKYGBgCgpgYGB7cn0KZ2dwbG90KGRfZXhwLCBhZXMoeCA9IEtMRjQsIHkgPSBSVU5YMSkpICsgCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBTYW1wbGUpKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikgKwogIHRoZW1lX2NsYXNzaWMoKQpgYGAKCiMgRXhjZXJjaXNlcwpGaXJzdCBsZXQgdXMgbG9vayBhdCB0aGUgbXBnIGRhdGFzZXQKYGBge3J9Cm1wZwpgYGAKCgojIyBFeGNlcmNpc2UgIzEKMS4gSG93IHdvdWxkIHlvdSBwbG90IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBgY3R5YCwgdGhlIGF2ZXJhZ2UgY2l0eSBtaWxlYWdlLCBhbmQgYGh3eWAsIHRoZSBhdmVyYWdlIGhpZ2h3YXkgbWlsZWFnZT8gSG93IHdvdWxkIHlvdSBkZXNjcmliZSB0aGlzIHJlbGF0aW9uc2hpcD8KCgo8ZGV0YWlscz48c3VtbWFyeT5DbGljayBoZXJlIGZvciBhbnN3ZXI8L3N1bW1hcnk+CgpgYGB7ciBjb2RlMX0KZ2dwbG90KG1wZywgYWVzKGN0eSwgaHd5KSkgKyAKICBnZW9tX3BvaW50KCkgKwogIHRoZW1lX2NsYXNzaWMoKQpgYGAKCjwvZGV0YWlscz4KCjxicj4KCjIuIEhvdyB3b3VsZCB5b3UgZHJhdyBhIHNtb290aCBsaW5lPyBBbHNvLCBob3cgd291bGQgeW91IGNvbG91ciB0aGUgdHJhbnNtaXNzaW9ucyBkaWZmZXJlbnRseT8KCjxkZXRhaWxzPjxzdW1tYXJ5PkNsaWNrIGhlcmUgZm9yIGFuc3dlcjwvc3VtbWFyeT4KYGBge3IgY29kZTFfMX0KZ2dwbG90KG1wZywgYWVzKGN0eSwgaHd5KSkgKyAKICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBkcnYpKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikgKwogIHRoZW1lX2NsYXNzaWMoKQpgYGAKCjwvZGV0YWlscz4KCgojIyBFeGNlcmNpc2UgIzIKU2hvdyB0aGUgZGlzdHJpYnV0aW9uIG9mIHNjb3JlcyBmb3IgYGh3eWAsIGhpZ2h3YXkgbWlsZXMgcGVyIGdhbGxvbgoKCjxkZXRhaWxzPjxzdW1tYXJ5PkNsaWNrIGhlcmUgZm9yIGFuc3dlcjwvc3VtbWFyeT4KCmBgYHtyIGNvZGUyfQpnZ3Bsb3QobXBnLCBhZXMoaHd5KSkgKyAKICBnZW9tX2hpc3RvZ3JhbSgpCmBgYAoKPC9kZXRhaWxzPgoKIyMgRXhjZXJjaXNlICMzClByb2R1Y2UgYSBzaW1pbGFyIHBsb3QgYXMgc2hvd24gYmVsb3cKCmBgYHtyLCBlY2hvPUZBTFNFfQpnZ3Bsb3QobXBnLCBhZXMoeD1kaXNwbCwgeT1jdHksIGNvbG91cj1kcnYsIHNpemU9Y3lsKSkgKwogIGdlb21fcG9pbnQoKSArIHRoZW1lX2NsYXNzaWMoKQpgYGAKCjxkZXRhaWxzPjxzdW1tYXJ5PkNsaWNrIGhlcmUgZm9yIGFuc3dlcjwvc3VtbWFyeT4KCmBgYHtyIGNvZGUzfQpnZ3Bsb3QobXBnLCBhZXMoeD1kaXNwbCwgeT1jdHksIGNvbG91cj1kcnYsIHNpemU9Y3lsKSkgKwogIGdlb21fcG9pbnQoKSArIHRoZW1lX2NsYXNzaWMoKQpgYGAKCjwvZGV0YWlscz4KCgo=